package example.demo.blockqueue;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 阻塞队列
 * 什么是队列？ 先进先出,插入在一端,删除在另一端。就像排队一样
 *
 * @param <E>
 */
public class CostumerArrayBlockQueue<E> {
    private Object[] elements;
    /**
     * <? 使用int绝逼线程不安全，为什么因为pop与push的线程都会对size作用
     */
    private AtomicInteger size;

    /**
     * <? 这个只有pop的线程才会对popIdx修改
     */
    private int popIdx;
    /**
     * <? 这个只有push的线程才会对pushIdx修改
     */
    private int pushIdx;

    private final Object popLock = new Object();
    private final Object pushLock = new Object();

    public CostumerArrayBlockQueue(int maxSize) {
        elements = new Object[maxSize];
        this.size = new AtomicInteger(0);
    }

    public CostumerArrayBlockQueue() {
        this(4);
    }

    public void push(E e) throws InterruptedException {
        int c = -1;
        synchronized (pushLock) {
            while (size.get() == elements.length) {
                pushLock.wait();
            }
            elements[pushIdx = ((pushIdx++) % elements.length)] = e;
//            size++;
            c = size.incrementAndGet();
            if (c < elements.length) {
                /**<? 这里就唤醒一个push线程*/
                pushLock.notify();
            }
        }
        /**<? 唤醒pop线程*/
        synchronized (popLock) {
//            popLock.notifyAll();
            popLock.notify();
        }
    }

    public E pop() throws InterruptedException {
        E e = null;
        int c = -1;
        synchronized (popLock) {
            while (size.get() == 0) {
                popLock.wait();
            }
            e = (E) elements[popIdx = ((popIdx++) % elements.length)];
//            size--;
            c = size.decrementAndGet();
            if (c > 0) {
                popLock.notify();
            }
        }
        /**<? 唤醒push线程*/
        synchronized (pushLock) {
//            pushLock.notifyAll();
            pushLock.notify();
        }
        return e;
    }

    public int getSize() {
        return size.get();
    }

    public static void main(String[] args) throws InterruptedException {
        CostumerArrayBlockQueue<String> strings = new CostumerArrayBlockQueue<>(2);
        for (int i = 0; i < 3; i++) {
            int finalI = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "----start");
                try {
                    strings.push("wahaha" + finalI);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "----end");
            }).start();
        }

        TimeUnit.SECONDS.sleep(3);
        for (int i = 0; i < 1; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "----start");
                try {
                    System.out.println(Thread.currentThread().getName() + "===strings.pop() = " + strings.pop());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "----end");
            }).start();
        }

    }
}
